package com.kzw.codegen;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.ManyToOne;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;

import com.kzw.core.mapper.Jackson;
import com.kzw.misc.annotation.S;
import com.kzw.misc.annotation.T;
import com.kzw.misc.bean.Bean;
import com.kzw.oa.system.model.Dictionary;

import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;

@SuppressWarnings({"rawtypes", "unchecked"})
public class CodeGen {
	
	// 需要生成的包名
	public static final String basePackage = "com.kzw.oa.blog";
	// 所有的实体类名
	public static List<Class<?>> models = getAllModel();
	
	// 模块名称
	public static String module = "blog";
	// 作者
	public static String author = "kzwang";
	// 生成目录
	public static String outputDir = "tmp";
	
	
	/**
	 * 获得所有实体类
	 * */
	public static List<Class<?>> getAllModel() {
		List<Class<?>> models = new ArrayList<>();
		try {
			String basePath = basePackage.replaceAll("\\.", "/");
			String path = CodeGen.class.getResource("/" + basePath + "/model").toURI().getPath();
			File dir = new File(path);
			String[] list = dir.list();
			for(String fname : list) {
				if(fname.endsWith(".class")) {
					String className = basePackage + ".model." + FilenameUtils.getBaseName(fname);
					Class<?> clazz = Class.forName(className);
					models.add(clazz);
				}
			}		
		} catch(Exception e) {
			e.printStackTrace();
		}
		return models;
	}
	
	public static void main(String[] args) throws Exception {
		Map<String, Object> dataMap = new HashMap<>();
		dataMap.put("head", "<#assign base=request.contextPath />\r\n<#import \"/tags/select.ftl\" as k />");
		dataMap.put("package", basePackage);
		dataMap.put("module", module);
		dataMap.put("author", author);
		
		
		String path = CodeGen.class.getResource("tpl").toURI().getPath();
		Configuration cfg = new Configuration(Configuration.VERSION_2_3_28);
		cfg.setDefaultEncoding("utf-8");
		cfg.setDirectoryForTemplateLoading(new File(path));
		cfg.setObjectWrapper(new DefaultObjectWrapper(Configuration.VERSION_2_3_28));
		
		
		// 生成service		
		genService(cfg, dataMap);
		
		// 生成action
		genAction(cfg, dataMap);
		
		// 生成view页面
		genViewPage(cfg, dataMap);
		
		// 生成form页面
		genFormPage(cfg, dataMap);
	}
	
	/**
	 * 生成form页面
	 * */
	public static void genFormPage(Configuration cfg, Map<String, Object> dataMap) {
		try {			
			Template temp = cfg.getTemplate("form.html");
			
			for(Class<?> entity : models) {
				String entityName = entity.getSimpleName();
				dataMap.put("EntityClass", entityName);
				// 首字母小写
				dataMap.put("entityClass", entityName.substring(0, 1).toLowerCase() + entityName.substring(1));
				dataMap.put("entityPath", entityName.toLowerCase());
				
				// 查询项
				List<Bean> items = new ArrayList<>();
				// 日期类型
				List<Bean> dateList = new ArrayList<>();
				// 数据字典类型
				List<Bean> dictList = new ArrayList<>();
				// 枚举下拉选择项
				List<Bean> selectList = new ArrayList<>();
				// 自定义类型下拉选择项
				List<Bean> beanList = new ArrayList<>();
				
				Field[] fields = entity.getDeclaredFields();
				for(Field f : fields) {					
					// 构建表单字段
					S s = f.getAnnotation(S.class);
					T t = f.getAnnotation(T.class);
					if(t != null) {
						if(f.getType() == Date.class) {
							dateList.add(new Bean(f.getName(), t.value()));
						} else if(f.getType() == Dictionary.class) {
							dictList.add(new Bean(f.getName() + ".dicId", t.value()));
						} else {
							// 字段上是否有@ManyToOne注解
							if(f.isAnnotationPresent(ManyToOne.class)) {
								// 默认主键为oid, 显示字段为name
								String model = f.getType().getSimpleName();
								beanList.add(new Bean(f.getName() + ".oid", t.value(), model));								
							} else {
								// 普通字段
								if(s!=null && !s.value().equals("")) {
									selectList.add(new Bean(f.getName(), t.value(), s.value()));
								} else {
									items.add(new Bean(f.getName(), t.value()));
								}
							}
						}
					}
				}
				
				dataMap.put("items", items);
				dataMap.put("dateList", dateList);
				dataMap.put("dictList", dictList);
				dataMap.put("selectList", selectList);
				dataMap.put("beanList", beanList);
				
				// 将模板和数据模型合并生成文件
				String model = StringUtils.substringAfterLast(basePackage, ".");
				File dir = new File(outputDir, model + "/pages").getCanonicalFile();
				FileUtils.forceMkdir(dir);
				File file = new File(dir, entityName.toLowerCase() + "_form.html");
				Writer docout = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
				temp.process(dataMap, docout);
				docout.close();
			}
		} catch(Exception e) {
			e.printStackTrace();
		}
		
	}
	
	
	/**
	 * 生成view页面
	 * */
	public static void genViewPage(Configuration cfg, Map<String, Object> dataMap) {
		try {			
			Template temp = cfg.getTemplate("view.html");
			
			// label的宽度
			Map<Integer, Integer> widthMap = new HashMap<>();
			widthMap.put(1, 45);
			widthMap.put(2, 60);
			widthMap.put(3, 75);
			widthMap.put(4, 90);
			widthMap.put(5, 105);
			widthMap.put(6, 120);
			
			for(Class<?> entity : models) {
				String entityName = entity.getSimpleName();
				dataMap.put("EntityClass", entityName);
				// 首字母小写
				dataMap.put("entityClass", entityName.substring(0, 1).toLowerCase() + entityName.substring(1));
				dataMap.put("entityPath", entityName.toLowerCase());
				
				// 查询项
				List<Bean> items = new ArrayList<>();
				// 日期类型
				List<Bean> dateList = new ArrayList<>();
				// 数据字典类型
				List<Bean> dictList = new ArrayList<>();
				// 枚举下拉选择项
				List<Bean> selectList = new ArrayList<>();
				
				// 表格的表头
				List<Bean> cols = new ArrayList<>();
				
				Field[] fields = entity.getDeclaredFields();
				for(Field f : fields) {
					// 1、构建查询条件
					if(f.isAnnotationPresent(S.class)) {
						S s = f.getAnnotation(S.class);
						T t = f.getAnnotation(T.class);
						if(t != null) {
							if(f.getType() == Date.class) {
								dateList.add(new Bean("Q_GED_" + f.getName(), t.value(), widthMap.get(t.value().length()), f.getName()+"_1"));
								dateList.add(new Bean("Q_LED_" + f.getName(), "~", widthMap.get(t.value().length()), f.getName()+"_2"));
							} else if(f.getType() == Dictionary.class) {
								dictList.add(new Bean("Q_EQL_" + f.getName() + ".dicId", t.value(), widthMap.get(t.value().length())));
							} else {
								if(!s.value().equals("")) {
									if(f.getType()==Integer.class || f.getType()==int.class) {
										selectList.add(new Bean("Q_EQI_" + f.getName(), t.value(), widthMap.get(t.value().length()), s.value()));
									} else if(f.getType()==Long.class || f.getType()==long.class) {
										selectList.add(new Bean("Q_EQL_" + f.getName(), t.value(), widthMap.get(t.value().length()), s.value()));
									} else {
										selectList.add(new Bean("Q_EQS_" + f.getName(), t.value(), widthMap.get(t.value().length()), s.value()));
									}
								} else {
									items.add(new Bean("Q_LIKES_" + f.getName(), t.value(), widthMap.get(t.value().length())));
								}
							}							
						}
					}
					
					// 2、构建表格字段
					T t = f.getAnnotation(T.class);
					if(t != null) {
						// 是否增加templet
						String templet = "";
						// 如果是枚举值
						if(f.isAnnotationPresent(S.class)) {
							S s = f.getAnnotation(S.class);
							if(StringUtils.isNotBlank(s.value())) {
								String val = s.value();
								val = val.replace("'", "\"");
								try {
									// 解析出来: [[0, '男'], [1, '女'], [2, '待定']]
									List<List> list = Jackson.me().fromJson(val, List.class);
									for(List ls : list) {
										String str = String.format("if(d.%s=='%s') return '%s';", f.getName(), ls.get(0)+"", ls.get(1)+"");
										templet += str;
									}
								} catch (Exception e) {}							
							}
						}						
						// 如果是数据字典类型
						if(f.getType() == Dictionary.class) {
							templet = String.format("return d.%s?d.%s.itemValue:''", f.getName(), f.getName());
						} else {
							// 字段上是否有@ManyToOne注解
							if(f.isAnnotationPresent(ManyToOne.class)) {
								templet = String.format("return d.%s?d.%s.name:''", f.getName(), f.getName());
							}
						}
						
						if(!templet.equals("")) {
							cols.add(new Bean(t.value(), f.getName(), templet));
						} else {
							cols.add(new Bean(t.value(), f.getName()));
						}						
					}
				}
				
				dataMap.put("items", items);
				dataMap.put("dateList", dateList);
				dataMap.put("dictList", dictList);
				dataMap.put("selectList", selectList);
				dataMap.put("cols", cols);
				
				// 将模板和数据模型合并生成文件
				String model = StringUtils.substringAfterLast(basePackage, ".");
				File dir = new File(outputDir, model + "/pages").getCanonicalFile();
				FileUtils.forceMkdir(dir);
				File file = new File(dir, entityName.toLowerCase() + "_view.html");
				Writer docout = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
				temp.process(dataMap, docout);
				docout.close();
			}
		} catch(Exception e) {
			e.printStackTrace();
		}
		
	}
	
	/**
	 * 生成service
	 * */
	public static void genService(Configuration cfg, Map<String, Object> dataMap) {
		try {
			Template temp = cfg.getTemplate("service.html");
			
			for(Class<?> entity : models) {
				String entityName = entity.getSimpleName();
				dataMap.put("EntityClass", entityName);
				
				// 将模板和数据模型合并生成文件
				String model = StringUtils.substringAfterLast(basePackage, ".");
				File dir = new File(outputDir, model + "/service").getCanonicalFile();
				FileUtils.forceMkdir(dir);
				File file = new File(dir, entityName + "Service.java");
				Writer docout = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
				temp.process(dataMap, docout);
				docout.close();
			}			
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 生成action
	 * */
	public static void genAction(Configuration cfg, Map<String, Object> dataMap) {
		try {
			Template temp = cfg.getTemplate("action.html");
			
			for(Class<?> entity : models) {
				String entityName = entity.getSimpleName();
				dataMap.put("EntityClass", entityName);
				// 首字母小写
				dataMap.put("entityClass", entityName.substring(0, 1).toLowerCase() + entityName.substring(1));
				dataMap.put("entityPath", entityName.toLowerCase());
				
				
				// 将模板和数据模型合并生成文件
				String model = StringUtils.substringAfterLast(basePackage, ".");
				File dir = new File(outputDir, model + "/web").getCanonicalFile();
				FileUtils.forceMkdir(dir);
				File file = new File(dir, entityName + "Action.java");
				Writer docout = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
				temp.process(dataMap, docout);
				docout.close();
			}			
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
}
